<?php

namespace App\Kuafu\Mybank\Service;

use App\Exceptions\BaseException;
use App\Kuafu\Base\Enum\BaseCodeEnum;
use App\Kuafu\Base\Service\BaseService;
use App\Kuafu\Mybank\Enum\MybankAccountEnum;
use App\Kuafu\Mybank\Model\MybankBillRecordBindModel;
use App\Kuafu\Mybank\Model\MybankBillRecordModel;
use App\Kuafu\Mybank\Model\MybankEntryModel;
use App\Kuafu\Mybank\Model\MybankOrderRecordModel;
use App\Kuafu\Mybank\Model\MybankOrderRecordWithholdMappingsModel;
use App\Kuafu\Mybank\Model\MybankOrderWithholdRecordModel;
use App\Kuafu\Mybank\Model\MybankOrderWithholdRefundRecordModel;
use App\Kuafu\Mybank\Model\MybankWalletRecordModel;
use App\Library\FeiShu\SendFs;
use App\Library\SnowFlake\SnowFlake;
use App\Remote\Mybank\MybankCloudFundsRemote;
use App\Remote\Mybank\Remote;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use function Symfony\Component\String\s;

/**
 * 协议代扣新版
 */
class MybankOrderWithholdV2Service
{
    public string $shopId;

    public string $merchantId;

    public int $payType = MybankOrderWithholdV2Service::PAY_TYPE_FREEZE; // 1 冻结金额 2 可用金额 3 冻结+可用

    public const PAY_TYPE_FREEZE = 1; // 冻结金额
    public const PAY_TYPE_PAY    = 2; // 可用金额
    public const PAY_TYPE_ALL    = 3; // 冻结+可用 [未实现 勿用]

    public function __construct(string $shopId = '', string $merchantId = '')
    {
        $this->merchantId = $merchantId;
        $this->shopId     = $shopId;
        $this->payType    = MybankOrderWithholdV2Service::PAY_TYPE_FREEZE;
    }

    private array $merchantBalanceList = [];

    /**
     * 获取商户余额信息
     *
     * @throws GuzzleException
     */
    public function getMerchantBalanceInfo(string $merchantId = '', bool $forceRefresh = false)
    {
        if (empty($merchantId)) {
            $merchantId = $this->merchantId;
        }

        if ($forceRefresh || empty($this->merchantBalanceList[$merchantId])) {
            $balanceInfo                            = MybankService::getMerchantBalance($merchantId);
            $this->merchantBalanceList[$merchantId] = $balanceInfo;
        }

        return $this->merchantBalanceList[$merchantId];
    }

    /**
     * 获取商户总余额(可用+冻结)
     *
     * @throws GuzzleException
     */
    public function getMerchantBalance(string $merchantId = ''): int
    {
        $balanceInfo = self::getMerchantBalanceInfo($merchantId);
        return (int)bcadd($balanceInfo['BALANCE_FREEZE'], $balanceInfo['PAY']);
    }

    /**
     * 获取冻结金额
     *
     * @throws GuzzleException
     */
    public function getBalanceFreeze(string $merchantId = ''): int
    {
        $balanceInfo = self::getMerchantBalanceInfo($merchantId);
        return (int)$balanceInfo['BALANCE_FREEZE'];
    }

    /**
     * 获取可用余额
     *
     * @throws GuzzleException
     */
    public function getBalancePay(string $merchantId = ''): int
    {
        if (empty($merchantId)) {
            $merchantId = $this->merchantId;
        }
        $balanceInfo = self::getMerchantBalanceInfo($merchantId);
        return (int)$balanceInfo['PAY'];
    }

    /**
     * 获取能支付的余额
     *
     * @return int
     * @throws GuzzleException
     */
    public function getPayBalance(): int
    {
        switch ($this->payType) {
            case MybankOrderWithholdV2Service::PAY_TYPE_FREEZE:
                return $this->getBalanceFreeze();
            case MybankOrderWithholdV2Service::PAY_TYPE_ALL:
                return $this->getBalancePay() + $this->getBalanceFreeze();
            case MybankOrderWithholdV2Service::PAY_TYPE_PAY:
            default:
                return $this->getBalancePay();
        }
    }

    /**
     * 分账批次后的处理
     *
     * @return void
     * @throws GuzzleException
     */
    public function withholdFzApply(string $batchNo)
    {
        // 获取批次的账单信息
        $recordBindList = MybankBillRecordBindModel::query()
                                                   ->with(['billRecordRel'])
                                                   ->where('batchNo', $batchNo)
                                                   ->where('flagMark', MybankBillRecordBindModel::MATCH_FLAG_MARK)
                                                   ->get();

        foreach ($recordBindList as $recordBind) {

            Log::channel('mybank')->info('$batchNo_withholdFzApply_log1', [
                '$batchNo' => $batchNo,
            ]);

            if (empty($recordBind['billRecordRel'])) {
                logMsg('批次对应账单不存在', [
                    '$recordBind' => $recordBind,
                ], true, true, SendFs::ROBOT_BATCH_BILL_MATCH);

                continue;
            }

            $billRecordRel = $recordBind['billRecordRel'];

            $this->shopId     = $billRecordRel['shopId'];
            $this->merchantId = $recordBind['merchantId'];

            Log::channel('mybank')->info('$batchNo_withholdFzApply_log2', [
                '$batchNo'   => $batchNo,
                'shopId'     => $this->shopId,
                'merchantId' => $this->merchantId,
            ]);

            /**
             * 添加钱包记录 [饿了么提现支出 ..... 等等]
             */
            MybankOrderWithholdService::walletRecordAdd(
                $this->merchantId,
                MybankWalletRecordModel::EARNING_TRANS_TYPE,
                $billRecordRel->transAmount,
                $this->getMerchantBalance(),
                $this->shopId,
                $recordBind['id'], // $recordId
                MybankWalletRecordModel::RECORD_ID_TYPE_BILL_RECORD_ID, // $recordIdType
                MybankWalletRecordModel::PLATFORM_TYPE_DEFAULT, // $platformType
                MybankBillRecordModel::MAPPING_SOURCE[$billRecordRel['source']],
                $billRecordRel['transId'],
                '',
                $recordBind['completeTime'],
                MybankWalletRecordModel::SUCCESS_STATUS,
                MybankWalletRecordModel::SHOW_RECORD
            );

            /**
             * 生成代付账单
             */
            $orderRecord = self::genCurrentBillRecord(
                $recordBind['billStartAt'],
                $recordBind['billEndAt'],
                $billRecordRel['source'],
                $billRecordRel['transId']
            );

            Log::channel('mybank')->info('$batchNo_withholdFzApply_log3', [
                '$batchNo'     => $batchNo,
                'shopId'       => $this->shopId,
                'merchantId'   => $this->merchantId,
                '$orderRecord' => $orderRecord->orderId ?? ''
            ]);

            /**
             * 生成代付关联信息
             */
            list($withholdBrokerage, $insertOrderWithholdMapping) = $this->genOrderWithholdFreezeMapping($orderRecord);

            Log::channel('mybank')->info('$batchNo_withholdFzApply_log4', [
                '$batchNo'                    => $batchNo,
                'shopId'                      => $this->shopId,
                'merchantId'                  => $this->merchantId,
                '$withholdBrokerage'          => $withholdBrokerage,
                '$insertOrderWithholdMapping' => json_encode($insertOrderWithholdMapping)
            ]);

            /**
             * 生成批次的协议代扣
             */
            $payeeInfo = [
                'payeeId'   => Remote::getIsvOrgId(),
                'payeeType' => MybankOrderWithholdRecordModel::WITHHOLD_PLATFORM,
            ];
            self::genBatchWithholdRecord($withholdBrokerage, $batchNo, $billRecordRel['id'],
                $insertOrderWithholdMapping, $payeeInfo);

            /**
             * 更新账单
             */
            self::afterWithholdApply($orderRecord, $billRecordRel, $recordBind);

            Log::channel('mybank')->info('$batchNo_withholdFzApply_log_end', [
                '$batchNo' => $batchNo,
            ]);
        }
    }

    /**
     * @throws GuzzleException
     */
    public function checkBalanceIsEnough(int $amount): bool
    {
        $balance = $this->getPayBalance();
        if ($balance < $amount) {
            return false;
        }
        return true;
    }


    /**
     * 一步生成账单、账单关联、订单划款(协议代扣)
     *
     * @param int $userId 用户 id
     * @param int $billAmount 金额
     * @param string $tranId 唯一交易号
     * @param int $type 类型 佣金, 订货, 饿了么欠款, 美团欠款
     * @param string $billTitle 账单 title
     * @param string $billRemark 账单 备注
     * @param array $payeeInfo 收款方
     * @return array
     * @throws BaseException
     * @throws GuzzleException
     */
    public function oneStepGenWithholdOrder(
        int $userId,
        int $billAmount,
        string $tranId,
        int $type = MybankOrderRecordModel::TYPE_KFOS_ORDER,
        string $billTitle = '',
        string $billRemark = '',
        array $payeeInfo = []
    ): array {
        if (!$this->lockOrder($tranId, MybankOrderRecordWithholdMappingsModel::TRAN_TYPE_PAY, 5)) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '该订单正在生成中, 请勿重复点击');
        }

        self::setMerchantIdByUserId($userId);

        if (empty($this->merchantId)) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '商户号不存在');
        }

        // 判断原订单号是否存在
        $orderRecord = MybankOrderRecordModel::query()->where('tranId', $tranId)->first();
        if (!empty($orderRecord)) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '订单已存在, 请勿重复生成');
        }

        /**
         * 生成账单
         */
        $orderRecord = $this->genOrderRecord($billRemark, $billAmount, $tranId, $billTitle, $type);

        /**
         * 支付类型 手动置为可用资金
         */
        $this->payType = MybankOrderWithholdV2Service::PAY_TYPE_PAY;

        /**
         * 账户余额是否够支付
         */
        if (!$this->checkBalanceIsEnough($billAmount)) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '金额不足请充值');
        }

        /**
         * 生成代付关联信息
         */
        list($withholdBrokerage, $insertOrderWithholdMapping) = $this->genOrderWithholdFreezeMapping($orderRecord);

        /**
         * 生成批次的协议代扣
         */
        if (empty($payeeInfo)) {
            $payeeInfo = MybankAccountEnum::getKfosOrderAccount();
        }
        $withHoldRecord = self::genBatchWithholdRecord($withholdBrokerage, '', '', $insertOrderWithholdMapping,
            $payeeInfo);


        return BaseService::returnData(BaseCodeEnum::OK, 'success', [
            'tranId'     => $tranId,
            'outTradeNo' => $withHoldRecord['outTradeNo']
        ]);

    }

    /**
     * 一键退款
     *
     * @param string $tranId 原夸父交易号
     * @param int $refundPrice 退款金额
     * @param string $refundReason 退款原因
     * @param string $outRefundNo 夸父退款单号
     * @return array
     * @throws BaseException
     * @throws GuzzleException
     */
    public function openStepGenRefundWithholdOrder(
        string $tranId,
        int $refundPrice,
        string $refundReason,
        string $outRefundNo = ''
    ): array {
        if (!$this->lockOrder($tranId . '_' . $outRefundNo, MybankOrderRecordWithholdMappingsModel::TRAN_TYPE_REFUND,
            5)) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '退款中 请勿重复退款');
        }

        if (empty($refundReason)) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '退款理由不能为空');
        }

        $orderRecord = MybankOrderRecordModel::query()->where('tranId', $tranId)->first();

        if ($refundPrice <= 0) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '退款金额不能小于等于 0');
        }

        $orderWithholdRecordList = $orderRecord->orderWithholdRecordHasManyThrough()->where('tranType', 1)->get();

        if (!count($orderWithholdRecordList)) {
            // 不存在对应的订单，对应报错
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '不存在对应的订单');
        }

        if (count($orderWithholdRecordList) != 1) {
            //  先只考虑 单笔支付单笔退款
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '暂不支持订单多支付退款');
        }

        if ($orderRecord->netPaidAmount < $refundPrice) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '退款金额不能大于已付款金额');
        }

        $withholdRecord = $orderWithholdRecordList[0];

        if (empty($outRefundNo)) {
            $outRefundNo = SnowFlake::nextIdCache();
        }
        // 创建退款代扣记录
        $withholdRefundModel                  = new MybankOrderWithholdRefundRecordModel();
        $withholdRefundModel->participantType = $withholdRecord->payeeType;
        $withholdRefundModel->participantId   = $withholdRecord->payeeId;
        $withholdRefundModel->relateOrderNo   = $withholdRecord->orderNo;
        $withholdRefundModel->relateTransNo   = $withholdRecord->operateNo;
        $withholdRefundModel->outRefundNo     = $outRefundNo;
        $withholdRefundModel->refundAmount    = $refundPrice;
        $withholdRefundModel->currency        = $withholdRecord->currency;
        $withholdRefundModel->refundReason    = $refundReason;
        $withholdRefundModel->status          = MybankOrderWithholdRecordModel::WITHHOLD_STATUS_DEFAULT;
        $withholdRefundModel->memo            = '';
        $withholdRefundModel->extInfo         = '';
        $withholdRefundModel->save();

        MybankOrderRecordWithholdMappingsModel::query()
                                              ->insert([
                                                  'orderWithholdRecordId' => $withholdRefundModel->id,
                                                  'orderRecordId'         => $orderRecord->orderId,
                                                  'amount'                => $refundPrice,
                                                  'tranType'              => MybankOrderRecordWithholdMappingsModel::TRAN_TYPE_REFUND,
                                                  'createdAt'             => time(),
                                                  'updatedAt'             => time(),
                                              ]);

        $withholdApplyResult = MybankCloudFundsRemote::withholdRefundApply(
            $withholdRefundModel->participantType,
            $withholdRefundModel->participantId,
            $withholdRefundModel->relateOrderNo,
            $withholdRefundModel->relateTransNo,
            $withholdRefundModel->outRefundNo,
            $withholdRefundModel->refundAmount,
            $withholdRefundModel->refundReason,
            $withholdRefundModel->extInfo,
            $withholdRefundModel->memo,
        );

        if (Remote::checkBodySuccess($withholdApplyResult)) {
            $withholdRefundModel->status         = MybankOrderWithholdRecordModel::WITHHOLD_STATUS_SUBMIT;
            $withholdRefundModel->refundOrderNo  = $withholdApplyResult['RefundOrderNo'] ?? '';
            $withholdRefundModel->submitDateTime = time();
            $withholdRefundModel->save();

        } else {
            // 异常报错
            logMsg('协议代扣退款异常', $withholdApplyResult, true, true, SendFs::ROBOT_BATCH_BILL_MATCH);
        }

        return BaseService::returnData(BaseCodeEnum::OK, '提交退款成功', [
            'outRefundNo' => $outRefundNo,
            'status'      => $withholdRefundModel->status
        ]);
    }

    /**
     * 查询协议代扣订单详情
     *
     * @param string $outTradeNo
     * @return array
     */
    public static function getWithholdOrderInfo(string $outTradeNo): array
    {
        $record = MybankOrderWithholdRecordModel::query()
                                                ->where('outTradeNo', $outTradeNo)
                                                ->first();

        if (empty($record)) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '不存在对应的订单');
        }

        $returnData = [
            'outTradeNo'     => $record->outTradeNo,
            'status'         => $record->status, // 退款状态 0; // 未提交 1; // 提交申请 2; // 成功 3; // 失败
            'errorCode'      => $record->errorCode,
            'errorDesc'      => $record->errorDesc,
            'finishDateTime' => $record->finishDateTime,
            'submitDateTime' => $record->submitDateTime,
        ];

        $code = BaseCodeEnum::OK;
        $msg  = '付款成功';

        if (!empty($record->errorCode)) {
            $code = str_pad((int)$record->errorCode, 6, 0);
            $msg  = '付款失败';
        }

        $returnData = array_filter($returnData);
        return BaseService::returnData($code, $msg, $returnData);
    }

    /**
     * 查询协议代扣退款订单详情
     *
     * @param string $outRefundNo
     * @return array
     */
    public static function getWithholdRefundOrderInfo(string $outRefundNo): array
    {
        $refundRecord = MybankOrderWithholdRefundRecordModel::query()
                                                            ->where('outRefundNo', $outRefundNo)
                                                            ->first();

        if (empty($refundRecord)) {
            return BaseService::returnData(BaseCodeEnum::ERR_400000, '不存在对应的订单');
        }
        $returnData = [
            'outRefundNo'      => $refundRecord->outRefundNo,
            'status'           => $refundRecord->status, // 退款状态 0; // 未提交 1; // 提交申请 2; // 成功 3; // 失败
            'errorCode'        => $refundRecord->errorCode,
            'errorDesc'        => $refundRecord->errorDesc,
            'refundFinishDate' => $refundRecord->refundFinishDate,
            'submitDateTime'   => $refundRecord->submitDateTime,
            'refundAmount'     => $refundRecord->refundAmount,
            'refundOrderNo'    => $refundRecord->refundOrderNo,
        ];

        $code = BaseCodeEnum::OK;
        $msg  = '退款成功';

        if (!empty($refundRecord->errorCode)) {
            $code = str_pad((int)$refundRecord->errorCode, 6, 0);
            $msg  = '退款失败';
        }

        $returnData = array_filter($returnData);
        return BaseService::returnData($code, $msg, $returnData);
    }


    /**
     * 通过 UserId 获取网商商户号
     *
     * @param int $userId
     * @return $this
     */
    public function setMerchantIdByUserId(int $userId): self
    {
        $entryList = MybankEntryModel::getEntryIdByMobile($userId, 0, MybankEntryModel::STATUS_3);
        if (empty($entryList)) {
            return $this;
        }
        $info             = collect($entryList)->first();
        $this->merchantId = $info['merchantId'];

        return $this;
    }

    /**
     * 生成当期账单
     *
     * @param string $startAt
     * @param string $endAt
     * @param int $source
     * @param string $transId
     * @return MybankOrderRecordModel|Builder|Model|object
     */
    private function genCurrentBillRecord(string $startAt, string $endAt, int $source, string $transId)
    {
        $billTimeList = [
            [
                'startAt' => $startAt,
                'endAt'   => $endAt
            ]
        ];

        // 计算当期金额
        $calcOrderBrokerSum = (string)MybankOrderWithholdService::calcBrokerSum($this->shopId, $billTimeList, $source);

        // 生成当期账单
        return self::genOrderRecord(
            vsprintf("账单周期[%s]: %s ~ %s",
                [
                    MybankBillRecordModel::MAPPING_SOURCE[$source],
                    date('Y-m-d', $startAt),
                    date('Y-m-d', $endAt),
                ]
            ),
            $calcOrderBrokerSum,
            $transId,
            '技术服务费',
            MybankOrderRecordModel::TYPE_BROKERAGE
        );

    }

    /**
     * 生成账单
     *
     * @param string $remark 备注
     * @param string $brokerage 金额
     * @param string $tranId 唯一交易号
     * @param string $title 标题
     * @param int $type 类型
     * @return MybankOrderRecordModel|Builder|Model|object
     */
    public function genOrderRecord(string $remark, string $brokerage, string $tranId, string $title, int $type)
    {
        return (new MybankOrderRecordModel())->genBillOrder(
            $this->shopId,
            $this->merchantId,
            $remark,
            $brokerage,
            $tranId,
            $title,
            $type
        );
    }


    public function getOrderWithholdLock(string $param): string
    {
        return 'getOrderWithholdLock:' . $param;
    }

    /**
     * 生成批次冻结账单关联
     *
     * @param MybankOrderRecordModel $orderRecord
     * @return array
     * @throws GuzzleException
     */
    private function genOrderWithholdFreezeMapping(MybankOrderRecordModel $orderRecord): array
    {
        // 此次代扣账单金额 是否 >= 冻结余额
        $insertOrderWithholdMapping = [];
        $calcOrderBrokerSum         = $orderRecord->brokerage;

        switch ($this->payType) {
            case MybankOrderWithholdV2Service::PAY_TYPE_PAY:
                $freezingBalance = $this->getBalancePay();
                break;
            case MybankOrderWithholdV2Service::PAY_TYPE_FREEZE:
            default:
                $freezingBalance = $this->getBalanceFreeze();
                break;
        }


        if (bccomp($calcOrderBrokerSum, $freezingBalance) >= 0) {
            // 余额不够扣
            // 代扣金额为当前余额
            $withholdBrokerage = $freezingBalance;

            $insertOrderWithholdMapping[] = [
                'orderRecordId' => $orderRecord->orderId,
                'amount'        => $withholdBrokerage
            ];
        } else {
            $withholdBrokerage = $calcOrderBrokerSum;
            // 余额够扣
            // 代扣金额为当前生成账单金额
            $insertOrderWithholdMapping[] = [
                'orderRecordId' => $orderRecord->orderId,
                'amount'        => $calcOrderBrokerSum
            ];

            // 如果是可用金额支付不用查历史账单
            if ($this->payType == MybankOrderWithholdV2Service::PAY_TYPE_PAY) {
                $unpaidBill = [];
            } else {

                $lockList = Redis::SMEMBERS($this->getOrderWithholdLock($orderRecord->shopId));

                // 查询历史是否有未支付的账单
                $unpaidBill = MybankOrderRecordModel::query()
                                                    ->where('type',
                                                        MybankOrderRecordModel::TYPE_BROKERAGE)
                                                    ->where('orderId', '!=', $orderRecord->orderId)
                                                    ->whereNotIn('orderId', $lockList)
                                                    ->where('payStatus', '!=',
                                                        MybankOrderRecordModel::PAY_STATUS_PAID)
                                                    ->where('completeAt', 0)
                                                    ->where('brokerage', '!=',
                                                        DB::raw('paidBrokerage'))
                                                    ->where('shopId', $orderRecord->shopId)
                                                    ->where('merchantId', $this->merchantId)
                                                    ->get();
            }


            // 可以用来支付佣金的金额
            $remainCanPay = bcsub($freezingBalance, $calcOrderBrokerSum);

            foreach ($unpaidBill as $bill) {
                $sillPay = bcsub($bill->brokerage, $bill->paidBrokerage);

                if (bccomp($remainCanPay, $sillPay) >= 0) {
                    // 小于余额 账单添加关联 继续循环
                    $remainCanPay = bcsub($remainCanPay, $sillPay);
                } else {
                    // 大于余额 停止循环
                    $sillPay      = $remainCanPay;
                    $remainCanPay = 0;
                }
                $insertOrderWithholdMapping[] = [
                    'orderRecordId' => $bill->orderId,
                    'amount'        => $sillPay
                ];

                Redis::SADD($this->getOrderWithholdLock($orderRecord->shopId), $bill->orderId);

                $withholdBrokerage = bcadd($withholdBrokerage, $sillPay);
                // 余额为 0
                if (bccomp($remainCanPay, 0) == 0) {
                    break;
                }
            }

            Redis::EXPIRE($this->getOrderWithholdLock($orderRecord->orderId), 3600);
        }

        return [$withholdBrokerage, $insertOrderWithholdMapping];
    }

    public function genOrderWithholdFreezeMappingTesting(MybankOrderRecordModel $orderRecord)
    {
        // 此次代扣账单金额 是否 >= 冻结余额
        $insertOrderWithholdMapping = [];

        //
        $calcOrderBrokerSum = '718';
        $freezingBalance    = '23997';

        if (bccomp($calcOrderBrokerSum, $freezingBalance) >= 0) {
            // 余额不够扣
            // 代扣金额为当前余额
            $withholdBrokerage = $freezingBalance;

            $insertOrderWithholdMapping[] = [
                'orderRecordId' => $orderRecord->orderId,
                'amount'        => $withholdBrokerage
            ];
        } else {
            $withholdBrokerage = $calcOrderBrokerSum;
            // 余额够扣
            // 代扣金额为当前生成账单金额
            $insertOrderWithholdMapping[] = [
                'orderRecordId' => $orderRecord->orderId,
                'amount'        => $calcOrderBrokerSum
            ];
            // 查询历史是否有未支付的账单
            $unpaidBill = [
                [
                    'brokerage'     => 1381,
                    'paidBrokerage' => 0,
                    'orderId'       => '11111',
                ],
            ];

            // 可以用来支付佣金的金额
            $remainCanPay = bcsub($freezingBalance, $calcOrderBrokerSum);

            foreach ($unpaidBill as $bill) {
                $sillPay = bcsub($bill['brokerage'], $bill['paidBrokerage']);

                if (bccomp($remainCanPay, $sillPay) >= 0) {
                    // 小于余额 账单添加关联 继续循环
                    $remainCanPay = bcsub($remainCanPay, $sillPay);
                } else {
                    // 大于余额 停止循环
                    $sillPay      = $remainCanPay;
                    $remainCanPay = 0;
                }
                $insertOrderWithholdMapping[] = [
                    'orderRecordId' => $bill['orderId'],
                    'amount'        => $sillPay
                ];
                $withholdBrokerage            = bcadd($withholdBrokerage, $sillPay);
                // 余额为 0
                if (bccomp($remainCanPay, 0) == 0) {
                    break;
                }
            }
        }


        dump($withholdBrokerage);
        dd($insertOrderWithholdMapping);
    }

    /**
     * 统一创建协议代扣
     *
     * @param string $brokerSum
     * @param string $batchNo
     * @param string $billRecordId
     * @param array $insertOrderWithholdMapping
     * @param array $payeeInfo
     * @return array|void
     * @throws BaseException
     * @throws GuzzleException
     */
    protected function genBatchWithholdRecord(
        string $brokerSum,
        string $batchNo,
        string $billRecordId,
        array $insertOrderWithholdMapping,
        array $payeeInfo
    ) {
        $outTradeNo = SnowFlake::nextIdCache();

        // 创建协议代扣记录
        $withholdModel               = new MybankOrderWithholdRecordModel();
        $withholdModel->outTradeNo   = $outTradeNo;
        $withholdModel->batchNo      = $batchNo;
        $withholdModel->billRecordId = $billRecordId;
        $withholdModel->scene        = MybankOrderWithholdRecordModel::WITHHOLD_SCENE_FREEZE;
        $withholdModel->totalAmount  = $brokerSum;
        if ($this->payType == self::PAY_TYPE_FREEZE) {
            $withholdModel->freezeAmount    = $brokerSum;
            $withholdModel->availableAmount = 0;
        } else {
            $withholdModel->freezeAmount    = 0;
            $withholdModel->availableAmount = $brokerSum;
        }

        $withholdModel->currency  = 'CNY';
        $withholdModel->payerId   = $this->merchantId;
        $withholdModel->payerType = MybankOrderWithholdRecordModel::WITHHOLD_MERCHANT;
        $withholdModel->payeeId   = $payeeInfo['payeeId'];
        $withholdModel->payeeType = $payeeInfo['payeeType'];
        $withholdModel->memo      = '';
        $withholdModel->save();

        // 添加账单和协议代扣的关联绑定
        foreach ($insertOrderWithholdMapping as &$mappingValues) {
            $mappingValues['orderWithholdRecordId'] = $withholdModel->id;
            $mappingValues['createdAt']             = time();
            $mappingValues['updatedAt']             = time();
        }

        if (!empty($insertOrderWithholdMapping)) {
            MybankOrderRecordWithholdMappingsModel::query()
                                                  ->insert($insertOrderWithholdMapping);
        }

        // 如果佣金计算为 0 则 不需要协议代扣
        if ($brokerSum != 0) {
            $withholdApplyResult = MybankCloudFundsRemote::withholdApply(
                $outTradeNo,
                $brokerSum,
                $withholdModel->freezeAmount,
                $withholdModel->availableAmount,
                $this->merchantId,
                $withholdModel->payeeId,
                $withholdModel->payeeType,
                $withholdModel->payerType,
                $withholdModel->scene
            );

            if (Remote::checkBodySuccess($withholdApplyResult)) {
                $withholdModel->status         = MybankOrderWithholdRecordModel::WITHHOLD_STATUS_SUBMIT;
                $withholdModel->orderNo        = $withholdApplyResult['OrderNo'] ?? '';
                $withholdModel->operateNo      = $withholdApplyResult['OperateNo'] ?? '';
                $withholdModel->submitDateTime = time();
                $withholdModel->save();

            } else {
                // 异常报错
                logMsg('协议代扣异常', $withholdApplyResult, true, true, SendFs::ROBOT_BATCH_BILL_MATCH);
                return;
            }
        } else {
            // 协议代扣 0 元直接 余额解冻
            (new MybankOrderWithholdNotifyService([
                'OutTradeNo' => $outTradeNo,
                'OutOrderNo' => $outTradeNo,
                'OrderNo'    => $outTradeNo,
                'OperateNo'  => $outTradeNo,
                'Status'     => 'SUCCESS',
                'FinishDate' => date('Y-m-d H:i:s')
            ]))->handle();
        }

        return [
            'outTradeNo' => $outTradeNo
        ];
    }

    protected function afterWithholdApply(
        MybankOrderRecordModel $orderRecord,
        Model $billRecordRel,
        MybankBillRecordBindModel $billRecordBindModel
    ) {
        $billTimeList = [
            [
                'startAt' => $billRecordBindModel['billStartAt'],
                'endAt'   => $billRecordBindModel['billEndAt']
            ]
        ];

        // 更新外卖表的数据
        MybankOrderWithholdService::updateBillRecordBillRecordBindId($this->shopId,
            $billRecordRel->id, $billRecordRel->source);

        MybankOrderWithholdService::updateOrderDivisionBillId($this->shopId, $billTimeList,
            $billRecordRel->source, $orderRecord->orderId);
    }

    /**
     * 返回锁
     *
     * @param string $id
     * @param string $tranType
     * @return string
     */
    private function getOrderLockKey(string $id, string $tranType): string
    {
        return sprintf('mybank_order:lock:%s:%s', $tranType, $id);
    }

    /**
     * 网商场景上锁 返回 是否成功上锁
     *
     * @param string $tranId
     * @param string $tranType
     * @param int $time
     * @return mixed
     */
    public function lockOrder(string $tranId, string $tranType, int $time)
    {
        $key = $this->getOrderLockKey($tranType, $tranId);
        $set = Redis::setNx($key, 'locked');

        if ($set) {
            Redis::expire($key, $time);
        }

        return $set;
    }
}
